import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Scanner;

import javax.media.opengl.GL;
import javax.media.opengl.glu.GLU;

import com.sun.opengl.impl.mipmap.Image;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureData;
import com.sun.opengl.util.texture.TextureIO;

public class Tree {
	GL gl;
	GLU glu;
	private int shaderprogram,list_id;
	private int leaf[],tree[];
	private int vert_shader, frag_shader;
	private float FPS;
	private int tree_num,tree_height,leaves_num;
	private Vector3D trees[];
	private Vector3D leaves[][];
	private Vector3D conus[];//основание дерева
	
	private static final int num_tex = 2;
	private Mipmap mipmap;
	public Tree(GL gl_, GLU glu_){
		gl=gl_;
		glu=glu_;
		mipmap = new Mipmap(gl,glu,num_tex);
		init();
		loadShader();
		CreateTexture(null);
	}
	private float distance(Vector3D a, Vector3D b) {
		Vector3D c = new Vector3D();
		c.x = a.x - b.x;
		c.y = a.y - b.y;
		c.z = a.z - b.z;
	    return (float) Math.sqrt((c.x()*c.x() + c.y()*c.y() + c.z()*c.z()));
	}
	
	private float distance2(Vector3D a, Vector3D b,int j) {
		Vector3D d = new Vector3D(0f,2f,0.5f);
		Matrix4x4 matz = new Matrix4x4();
		matz = Matrix4x4.set((float)Math.cos(a.x+Atmosphere.pi),(float)-Math.sin(a.x+Atmosphere.pi),0,0,
	    		(float)Math.sin(a.x+Atmosphere.pi),(float)Math.cos(a.x+Atmosphere.pi),0,0,
	                   0,0,1,0,
	                   0,0,0,1);
		d = Matrix4x4.mult(matz,d);
		d.x += trees[j].x;
		d.y += trees[j].y;
		d.z += trees[j].z;
		Vector3D c = new Vector3D();
		c.x = d.x - b.x;
		c.y = d.y - b.y;
		c.z = d.z - b.z;
	    return (float) Math.sqrt((c.x()*c.x() + c.y()*c.y() + c.z()*c.z()));
	}
	
	public Vector3D[] qSort(Vector3D[] A, int low, int high, Vector3D pos) {
	      int i = low;                
	      int j = high;
	      float x = distance(A[(low+high)/2],pos); 
	      do {
	          while(distance(A[i],pos) > x) ++i; 
	          while(distance(A[j],pos) < x) --j;  
	          if(i <= j){           	             
	              Vector3D temp = new Vector3D();//quick sort for sprites
	              temp = A[i];
	              A[i] = A[j];
	              A[j] = temp;	             
	              i++; j--;
	          }
	      } while(i < j);
	      if(low < j) A = qSort(A, low, j,pos);
	      if(i < high) A = qSort(A, i, high,pos);
	      return A;
	  }
	public Vector3D[] qSort2(Vector3D[] A, int low, int high, Vector3D pos,int tree_val) {
	      int i = low;                
	      int j = high;
	      float x = distance2(A[(low+high)/2],pos,tree_val); 
	      do {
	          while(distance2(A[i],pos,tree_val) > x) ++i; 
	          while(distance2(A[j],pos,tree_val) < x) --j;  
	          if(i <= j){           	             
	              Vector3D temp = new Vector3D();//quick sort for sprites
	              temp = A[i];
	              A[i] = A[j];
	              A[j] = temp;	             
	              i++; j--;
	          }
	      } while(i < j);
	      if(low < j) A = qSort2(A, low, j,pos,tree_val);
	      if(i < high) A = qSort2(A, i, high,pos,tree_val);
	      return A;
	  }

	private void init(){
		leaf = new int[1];
		tree = new int[1];
		tree_num = 4;
		list_id = 0;
		tree_height = 10;
		leaves_num = 70;//or 200 or fps
		trees = new Vector3D[tree_num];
		tree_init();//location of trees
		FPS = 1;
		conus = new Vector3D[9];// в основании дерева лежит восьмиугольник
		conus_init();
		leaves = new Vector3D[tree_num][leaves_num];
		leaves_init();
	}
	public static float rnd() {
		return (float)Math.random();
	}
	private void tree_init(){
	    for(int i = 0; i < tree_num; i++) {
	        trees[i] = new Vector3D();
	    }
	    trees[0].setXYZ(-40,-40,0);
	    trees[1].setXYZ(130,-40,0);
	    trees[2].setXYZ(-40,280,0);
	    trees[3].setXYZ(130,280,0);
	}
	private void leaves_init(){
		for(int j = 0; j < tree_num; j++) {
			for(int i = 0; i < leaves_num; i++) {
				leaves[j][i] = new Vector3D();
				if(j == 0) {
					leaves[j][i].x = rnd()*Atmosphere.pi*2f; //leaf yaw
					leaves[j][i].y = (rnd() + 0.5f)/1.5f; //leaf pitch
					leaves[j][i].z = rnd()*44f; //leaf height
				}
				else {
					leaves[j][i].x = leaves[j-1][i].x;
					leaves[j][i].y = leaves[j-1][i].y;
					leaves[j][i].z = leaves[j-1][i].z;
				}
			}
		}
	}
	private void conus_init(){
		float approx = (float)Math.sqrt(2f)/2f;//
	    conus[0] = new Vector3D(-1f,0f,0f);
	    conus[1] = new Vector3D(-approx,approx,0f);
	    conus[2] = new Vector3D(0f,1f,0f);
	    conus[3] = new Vector3D(approx,approx,0f);
	    conus[4] = new Vector3D(1f,0f,0f);
	    conus[5] = new Vector3D(approx,-approx,0f);
	    conus[6] = new Vector3D(0f,-1f,0f);
	    conus[7] = new Vector3D(-approx,-approx,0f);
	    conus[8] = new Vector3D(-1f,0f,0f);
	}
	private void CreateTexture(Image image)
	 {
		 URL imageURL= this.getClass().getResource(
				 "tree.png");
	       

		 gl.glEnable(GL.GL_TEXTURE_2D);
		 gl.glGenTextures(1,leaf, 0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, leaf[0]);
		 gl.glShadeModel(GL.GL_FLAT);
		 gl.glDepthFunc(GL.GL_LESS);
		 mipmap.makeImages(imageURL, 0);
		 mipmap.LoadImages(0);
		 
		 URL imageURL2= this.getClass().getResource(
				 "trunk.png");

		 gl.glGenTextures(1,tree,0);
		 gl.glBindTexture(GL.GL_TEXTURE_2D, tree[0]);
		 mipmap.makeImages(imageURL2, 1);
		 mipmap.LoadImages(1);

	 }
	
	 private void loadShader(){
		 vert_shader = gl.glCreateShader(GL.GL_VERTEX_SHADER);
	        frag_shader = gl.glCreateShader(GL.GL_FRAGMENT_SHADER);
	        
	        BufferedReader brv = null;
			try {
				brv = new BufferedReader(
						new FileReader(
								"src/lighting.vert"));
			} catch (FileNotFoundException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
	        String vsrc = "";
	        String line;
	        try {
				while ((line=brv.readLine()) != null) {
				  vsrc += line + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(vert_shader, 1,
	        		new String[] {vsrc}, (int[])null, 0);
	        gl.glCompileShader(vert_shader);
	        
	        BufferedReader brf = null;
			try {
				brf = new BufferedReader(
						new FileReader(
								"src/lighting.frag"));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        String fsrc = "";
	        String lines;
	        try {
				while ((lines=brf.readLine()) != null) {
				  fsrc += lines + "\n";
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
	        gl.glShaderSource(frag_shader, 1,
	        		new String[] {fsrc}, (int[])null, 0);
	        gl.glCompileShader(frag_shader);
	        
	        shaderprogram = gl.glCreateProgram();
	        gl.glAttachShader(shaderprogram, vert_shader);
	        gl.glAttachShader(shaderprogram, frag_shader);
	        gl.glLinkProgram(shaderprogram);
	        gl.glValidateProgram(shaderprogram);
	 }
	 private void setShader(Vector3D pos,int tex){
		 
		 	gl.glActiveTexture(GL.GL_TEXTURE1);
		    gl.glBindTexture(GL.GL_TEXTURE_2D,1);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "envMap"), 1);
	        
	        gl.glActiveTexture(GL.GL_TEXTURE0);
		    if(tex == 0) gl.glBindTexture(GL.GL_TEXTURE_2D,leaf[0]);
		    else gl.glBindTexture(GL.GL_TEXTURE_2D,tree[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "leaf"), 0);
	        gl.glActiveTexture(GL.GL_TEXTURE3);
	        gl.glBindTexture(GL.GL_TEXTURE_2D,Ground.shadowMap1[0]);
	        gl.glUniform1i(gl.glGetUniformLocation(
	        		shaderprogram, "shadowMap"), 3);
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "eyePos"), pos.x,pos.y,pos.z);
	        gl.glUniform3f(gl.glGetUniformLocation(
	        		shaderprogram, "lightPos"), Atmosphere.sun.x,Atmosphere.sun.y,Atmosphere.sun.z);
	 }
	 public static Vector3D normal(Vector3D A, Vector3D B, Vector3D C) {
		    float x = A.y()*(B.z() - C.z()) + B.y()*(C.z() - A.z()) + C.y()*(A.z() - B.z());
		    float y = A.z()*(B.x() - C.x()) + B.z()*(C.x() - A.x()) + C.z()*(A.x() - B.x());
		    float z = A.x()*(B.y() - C.y()) + B.x()*(C.y() - A.y()) + C.x()*(A.y() - B.y());
		    Vector3D result = new Vector3D(-x,-y,-z);
		    result.normalize();
		    return result;
		}
	 public void draw_leaves(Vector3D player, Clouds cloud, float p1, float p2, int p3,boolean shadow) {
		 if(shadow) gl.glColorMask(false, false, false, false);
				 Vector3D center = new Vector3D();
				 center = cloud.center;
		 		 gl.glActiveTexture(GL.GL_TEXTURE0);
		 		 trees = qSort(trees,0,tree_num-1,player);
		 		 for(int j = 0; j < tree_num; j++) {
		 			 leaves[j] = qSort2(leaves[j],0,leaves_num-1,player,j);
		 		 }
				 gl.glDisable(GL.GL_CULL_FACE);
				 gl.glEnable(GL.GL_ALPHA_TEST);
				 gl.glEnable(GL.GL_BLEND);
				 gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
				// gl.glDepthMask(false);
				 gl.glBindTexture(GL.GL_TEXTURE_2D,leaf[0]-1);
				 gl.glColor3f(1f,1f,1f);
				 Matrix4x4 matz = new Matrix4x4();
				 Vector3D point1 = new Vector3D(-2f,0.5f,2.5f);
				 Vector3D point2 = new Vector3D(0f,0f,3.5f);
				 Vector3D point3 = new Vector3D(2f,0.5f,2.5f);
				 Vector3D point4 = new Vector3D(-2f,4f,1.5f);
				 Vector3D point5 = new Vector3D(0f,4f,2.5f);
				 Vector3D point6 = new Vector3D(2f,4f,1.5f);
				 Vector3D st_point1 = new Vector3D(-2f,0.5f,2.5f);
				 Vector3D st_point2 = new Vector3D(0f,0f,3.5f);
				 Vector3D st_point3 = new Vector3D(2f,0.5f,2.5f); //start values
				 Vector3D st_point4 = new Vector3D(-2f,4f,1.5f);
				 Vector3D st_point5 = new Vector3D(0f,4f,2.5f);
				 Vector3D st_point6 = new Vector3D(2f,4f,1.5f);
				 //
				// gl.glBegin(GL.GL_QUADS);
				 float scale;
				 if(distance(trees[0],player) < distance(center,player) && !shadow) {
					 gl.glDisable(GL.GL_ALPHA_TEST);
					 gl.glDisable(GL.GL_BLEND);
				     gl.glEnable(GL.GL_CULL_FACE);	
				     cloud.draw(p1, p2, p3);
				     gl.glActiveTexture(GL.GL_TEXTURE0);
				     gl.glDisable(GL.GL_CULL_FACE);
					 gl.glEnable(GL.GL_ALPHA_TEST);
					 gl.glEnable(GL.GL_BLEND);
					 gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
					 gl.glBindTexture(GL.GL_TEXTURE_2D,leaf[0]-1);				     
				 }
				 for(int j = 0; j < tree_num; j++) {
					 if(j > 0)
					 if(!shadow && distance(trees[j-1],player) > distance(center,player) && distance(center,player) > distance(trees[j],player)) {
						 gl.glDisable(GL.GL_ALPHA_TEST);
						 gl.glDisable(GL.GL_BLEND);
					     gl.glEnable(GL.GL_CULL_FACE);	
					     cloud.draw(p1, p2, p3);
					     //gl.glActiveTexture(GL.GL_TEXTURE0);
					     gl.glDisable(GL.GL_CULL_FACE);
						 gl.glEnable(GL.GL_ALPHA_TEST);
						 gl.glEnable(GL.GL_BLEND);
						 gl.glBlendFunc(GL.GL_SRC_ALPHA,GL.GL_ONE_MINUS_SRC_ALPHA);
						// gl.glBindTexture(GL.GL_TEXTURE_2D,leaf[0]-1);
					 }
				//glList	
					 gl.glUseProgram(shaderprogram);
					 setShader(player,0);
					 	 if(!shadow)  gl.glDepthMask(false);
						 gl.glBegin(GL.GL_QUADS);
						 
						 
				for(int i = 0; i < leaves_num; i++) {
					 
					 scale = (50f - leaves[j][i].z)/10f;
					 matz = Matrix4x4.set((float)Math.cos(leaves[j][i].x+Atmosphere.pi),(float)-Math.sin(leaves[j][i].x+Atmosphere.pi),0,0,
					    		(float)Math.sin(leaves[j][i].x+Atmosphere.pi),(float)Math.cos(leaves[j][i].x+Atmosphere.pi),0,0,
					                   0,0,1,0,
					                   0,0,0,1);
	 			     point1 = Matrix4x4.mult(matz,point1);
	 			     point2 = Matrix4x4.mult(matz,point2);
	 			     point3 = Matrix4x4.mult(matz,point3);
	 			     point4 = Matrix4x4.mult(matz,point4);
	 			     point5 = Matrix4x4.mult(matz,point5);
	 			     point6 = Matrix4x4.mult(matz,point6);
	 			     
	 			     point1.x = point1.x*scale + trees[j].x;
	 			     point2.x = point2.x*scale + trees[j].x; //масштабируем листья и перемещаем на дерево
	 			     point3.x = point3.x*scale + trees[j].x;
	        		 point4.x = point4.x*scale + trees[j].x;
	 			     point5.x = point5.x*scale + trees[j].x;
	 			     point6.x = point6.x*scale + trees[j].x;
	 			     
	 			     point1.y = point1.y*scale + trees[j].y;
	 			     point2.y = point2.y*scale + trees[j].y;
	 			     point3.y = point3.y*scale + trees[j].y;
	        		 point4.y = point4.y*scale + trees[j].y;
	 			     point5.y = point5.y*scale + trees[j].y;
	 			     point6.y = point6.y*scale + trees[j].y;
	 			     
	 			     point1.z = point1.z*scale + leaves[j][i].z;
	 			     point2.z = point2.z*scale + leaves[j][i].z;
	 			     point3.z = point3.z*scale + leaves[j][i].z;
	 			     point4.z = point4.z*scale-1f/leaves[j][i].y + leaves[j][i].z;
	 			     point5.z = point5.z*scale-1f/leaves[j][i].y + leaves[j][i].z;
	 			     point6.z = point6.z*scale-1f/leaves[j][i].y + leaves[j][i].z;
	 			     
	 			     Vector3D pol_normal = new Vector3D();
	 			     pol_normal = normal(point2, point1, point4);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0f,0f);
	 			     gl.glVertex3f(point1.x,point1.y,point1.z);
	 			     
	 			     pol_normal = normal(point1, point4, point5);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0f,1f);
	 			     gl.glVertex3f(point4.x,point4.y,point4.z);
	 			     
	 			     pol_normal = normal(point1, point4, point6);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0.5f,1f);
	 			     gl.glVertex3f(point5.x,point5.y,point5.z);
	 			     
	 			     pol_normal = normal(point1, point4, point6);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0.5f,0f);
	 			     gl.glVertex3f(point2.x,point2.y,point2.z);
	 			     
	 			     pol_normal = normal(point1, point4, point6);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0.5f,0f);
	 			     gl.glVertex3f(point2.x,point2.y,point2.z);
	 			     
	 			     pol_normal = normal(point1, point4, point6);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(0.5f,1f);
	 			     gl.glVertex3f(point5.x,point5.y,point5.z);
	 			     
	 			     pol_normal = normal(point5, point6, point3);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(1f,1f);
	 			     gl.glVertex3f(point6.x,point6.y,point6.z);
	 			     
	 			     pol_normal = normal(point6, point3, point2);
	 			     gl.glNormal3f(pol_normal.x, pol_normal.y, pol_normal.z);
	 			     gl.glTexCoord2d(1f,0f);
	 			     gl.glVertex3f(point3.x,point3.y,point3.z);
	 			    
	 			     point1 = st_point1;
					 point2 = st_point2;
					 point3 = st_point3;
					 point4 = st_point4;
					 point5 = st_point5;
					 point6 = st_point6;
				 }	
				 
				 gl.glEnd();
				 if(!shadow)	 gl.glDepthMask(true);
				 gl.glUseProgram(0);
			//
			 }
		     //
			 gl.glDisable(GL.GL_ALPHA_TEST);
			 gl.glDisable(GL.GL_BLEND);
		     gl.glEnable(GL.GL_CULL_FACE);	
		     if(!shadow && distance(trees[tree_num-1],player) > distance(center,player)) {
		    	 cloud.draw(p1, p2, p3);
			 }
		     if(shadow) gl.glColorMask(true,true,true,true);
	 }
	 public void draw(Vector3D player) {
	 		FPS = MyJoglCanvas.fps();

	 			gl.glUseProgram(shaderprogram);
	 			setShader(player,1);


	 		gl.glDisable(GL.GL_CULL_FACE);
	 		
	 		gl.glBegin(GL.GL_QUADS);
	 		float scale = 5f;
	 		float offset = 0f;
	 		for(int a = 0; a < tree_num; a++) {
	 			for(int j = 0; j < tree_height; j++) {
		 			for(int i = 0; i < 8; i++) {
		 				float width = (float)2*(tree_height - j)/(float)tree_height;//ширина ствола
		 				float width1 = (float)2*(tree_height - j - 1)/(float)tree_height;
//		 				if(j == 6) {
//		 					gl.glTexCoord2d(0,0);
//			 				gl.glVertex3f(conus[i].x*width + trees[a].x,conus[i].y*width + trees[a].y,j*scale);
//			 				
//			 				gl.glTexCoord2d(1,0);
//			 				gl.glVertex3f(conus[i+1].x*width + trees[a].x,conus[i+1].y*width + trees[a].y,j*scale);
//			 				
//			 				gl.glTexCoord2d(1,1);
//			 				gl.glVertex3f(conus[i+1].x*width1 +offset+ trees[a].x,conus[i+1].y*width1+offset + trees[a].y,min(tree_height-1,(j+1))*scale);
//			 				
//			 				gl.glTexCoord2d(0,1);
//			 				gl.glVertex3f(conus[i].x*width1+offset + trees[a].x,conus[i].y*width1+offset + trees[a].y,min(tree_height-1,(j+1))*scale);
//		 				}
//		 				else if(j == 7) {
//		 					gl.glTexCoord2d(0,0);
//			 				gl.glVertex3f(conus[i].x*width+offset + trees[a].x,conus[i].y*width+offset + trees[a].y,j*scale);
//			 				
//			 				gl.glTexCoord2d(1,0);
//			 				gl.glVertex3f(conus[i+1].x*width+offset + trees[a].x,conus[i+1].y*width+offset + trees[a].y,j*scale);
//			 				
//			 				gl.glTexCoord2d(1,1);
//			 				gl.glVertex3f(conus[i+1].x*width1+ trees[a].x,conus[i+1].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale);
//			 				
//			 				gl.glTexCoord2d(0,1);
//			 				gl.glVertex3f(conus[i].x*width1 + trees[a].x,conus[i].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale);
//		 				}
		 				
		 				    Vector3D tree_normal = new Vector3D();
		 				    Vector3D a1 = new Vector3D(conus[i].x*width + trees[a].x,conus[i].y*width + trees[a].y,j*scale);
		 				    Vector3D a2 = new Vector3D(conus[i+1].x*width + trees[a].x,conus[i+1].y*width + trees[a].y,j*scale);
		 				    Vector3D a3 = new Vector3D(conus[i+1].x*width1+ trees[a].x,conus[i+1].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale);
		 				    Vector3D a4 = new Vector3D(conus[i].x*width1 + trees[a].x,conus[i].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale); 
		 					
		 				    Vector3D a1_;
		 				    Vector3D a2_;
		 				    Vector3D a3_;
		 				    
		 				    
		 				    if(i == 0) {
		 				    	a1_ = new Vector3D(conus[7].x*width + trees[a].x,conus[7].y*width + trees[a].y,j*scale);
		 				    }
		 				    else {
		 				    	a1_ = new Vector3D(conus[i-1].x*width + trees[a].x,conus[i-1].y*width + trees[a].y,j*scale);
		 				    }
		 				    if(i == 7) {
		 				    	a2_ = new Vector3D(conus[0].x*width + trees[a].x,conus[0].y*width + trees[a].y,j*scale);
			 				    a3_ = new Vector3D(conus[0].x*width1+ trees[a].x,conus[0].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale);
		 				    }
		 				    else {
		 				    	a2_ = new Vector3D(conus[i+1].x*width + trees[a].x,conus[i+1].y*width + trees[a].y,j*scale);
			 				    a3_ = new Vector3D(conus[i+1].x*width1+ trees[a].x,conus[i+1].y*width1 + trees[a].y,min(tree_height-1,(j+1))*scale);
		 				    }
		 				    tree_normal = normal(a1_,a2,a3);
		 				    gl.glNormal3f(tree_normal.x,tree_normal.y,tree_normal.z);
		 					gl.glTexCoord2d(0,0);
			 				gl.glVertex3f(a1.x,a1.y,a1.z);
			 				
			 				tree_normal = normal(a1,a2_,a3_);
			 				gl.glNormal3f(tree_normal.x,tree_normal.y,tree_normal.z);
			 				gl.glTexCoord2d(1,0);
			 				gl.glVertex3f(a2.x,a2.y,a2.z);
			 				
			 				tree_normal = normal(a1,a2_,a3_);
			 				gl.glNormal3f(tree_normal.x,tree_normal.y,tree_normal.z);
			 				gl.glTexCoord2d(1,1);
			 				gl.glVertex3f(a3.x,a3.y,a3.z);
			 				
			 				tree_normal = normal(a1_,a2,a3);
			 				gl.glNormal3f(tree_normal.x,tree_normal.y,tree_normal.z);
			 				gl.glTexCoord2d(0,1);
			 				gl.glVertex3f(a4.x,a4.y,a4.z);
		 				
		 			}
		 		}
	 		}
	 		gl.glEnd();
	 		gl.glEnable(GL.GL_CULL_FACE);

	 		gl.glUseProgram(0);
	}
	private float min(int a, int b) {
		return a > b ? b : a;
	}
	private float max(int a, int b) {
		return a > b ? a : b;
	}
}